#pragma once

#include "fxcc/core/graph/opengl3/Texture2DJack.h"
#include "fxcc/core/graph/opengl3/TextureCubeJack.h"
#include "fxcc/core/graph/opengl3/Shader.h"
#include "fxcc/core/graph/opengl3/FrameBuffer.h"
#include "fxcc/core/graph/opengl3/Effect3d.h"
#include "fxcc/core/graph/opengl3/MeshJack.h"
#include "fxcc/core/graph/opengl3/RenderBuffer.h"

namespace fxcc
{
    namespace graph
    {
        namespace opengl3
        {
            namespace pbrx
            {
                struct FXCC_API HdrTexture
                {
                    std::string m_Name;
                    std::shared_ptr<fxcc::graph::opengl3::TextureCubeJack> irradianceMap;
                    std::shared_ptr<fxcc::graph::opengl3::TextureCubeJack> prefilterMap;
                    std::shared_ptr<fxcc::graph::opengl3::TextureCubeJack> envCubeMap;
                    std::shared_ptr<fxcc::graph::opengl3::Texture2DJack> brdfLUTtexture;

                    std::shared_ptr<fxcc::graph::opengl3::Shader> equirectangularToCubemapShader;
                    std::shared_ptr<fxcc::graph::opengl3::Shader> irradianceShader;
                    std::shared_ptr<fxcc::graph::opengl3::Shader> prefilterShader;
                    std::shared_ptr<fxcc::graph::opengl3::Shader> brdfShader;

                    std::shared_ptr<fxcc::graph::opengl3::MeshJack> m_CubeMesh, m_QuadMesh;
                    std::shared_ptr<fxcc::graph::opengl3::FrameBuffer> m_FrameBuffer;
                    std::shared_ptr<fxcc::graph::opengl3::RenderBuffer> m_RenderBuffer;

                    std::string m_hdrPath;
                    struct   PathDesc
                    {
                        std::string envCubePath;
                        std::string brdfLUTTexturePath;
                        std::string irradiancePath;
                        std::string prefilterPath;
                    } m_PathDesc;
                    HdrTexture(const PathDesc& desc);
                    HdrTexture(const std::string hdrPath);

                    void SavePath(const PathDesc& pathDesc);

                    void InitTextures();

                    void InitEffects();

                    bool CreateHdrTexture();

                    static std::string CubeMapVertCode();
                    static std::string EquirectangluarToCubeMapFragCode();
                    static std::string IrradianceConvolutionFragCode();
                    static std::string BRDFFragCode();
                    static std::string BRDFVertCode();
                    static std::string PrefilterFragCode();

                private:
                    glm::mat4 captureProjection = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, 10.0f);
                    glm::mat4 captureViews[6] =
                    {
                        glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)),
                        glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)),
                        glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)),
                        glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f), glm::vec3(0.0f, 0.0f, -1.0f)),
                        glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(0.0f, -1.0f, 0.0f)),
                        glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, -1.0f), glm::vec3(0.0f, -1.0f, 0.0f)) };
                };
            }
        }
    }
}